#ifndef     __PROTO_DRV_H
#define     __PROTO_DRV_H

#include "../rtos/event_flags.h"
#include "../conn/frame.h"
#include <stdio.h>
#include <stdarg.h>

namespace pub_driver {

template <typename DEVICE, uint32_t WRITE_BUF_SIZE>
class ProtoDrv {

public:
    static constexpr uint32_t FLAG_CAN_TC = 0x01;

    static constexpr uint32_t FLAG_WRITE_FINISH = 0x10;
    static constexpr uint32_t FLAG_ACK = 0x20;

    void println(const char *format, va_list ap) {
        m_flags.wait_any_flags(FLAG_WRITE_FINISH);

        int n = vsprintf(
            reinterpret_cast<char *>(m_write_buf + 8),
            format,
            ap
        );
        m_write_buf[6] = conn::proto::pub::NOTIFY_LOG;
        m_write_buf[7] = n;
        uint8_t len = conn::make_data(
                m_write_buf, 
                conn::addr::IPC,
                2 + n
            );
        write_raw(m_write_buf, len);
    }

    void write(const uint8_t *buf, uint8_t len) {
        m_flags.wait_any_flags(FLAG_WRITE_FINISH);
        write_raw(buf, len);
    }
    
    void write_ack(uint8_t dst, uint8_t seq) {
        m_flags.wait_any_flags(FLAG_WRITE_FINISH);
        uint8_t len = conn::make_ack(m_write_buf, dst, seq);
        write_raw(m_write_buf, len);
    }

    void write_pong(uint8_t dst) {
        m_flags.wait_any_flags(FLAG_WRITE_FINISH);
        uint8_t len = conn::make_pong(m_write_buf, dst);
        write_raw(m_write_buf, len);
    }

protected:

    inline void wait_can_tc() {
        m_flags.wait_any_flags(FLAG_CAN_TC);
    }

public:
    inline void notify_can_tc() {
        m_flags.set_flags(FLAG_CAN_TC);
    }
	
    inline void notify_write_finish() {
        m_flags.set_flags(FLAG_WRITE_FINISH);
    }
    
private:
	inline void wait_write_finish() {
        m_flags.wait_any_flags(FLAG_WRITE_FINISH);
    }

    void write_raw(const uint8_t *buf, uint8_t len) {
        static_cast<DEVICE *>(this)->write_raw(buf, len);
    }
    uint8_t m_write_buf[WRITE_BUF_SIZE];
    rtos::EventFlags m_flags;
};



}

#endif
